package chen.web.controller.forum;

import java.util.HashMap;
import java.util.List;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validator;

import org.json.JSONException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;

import chen.web.config.ConfigRepository;
import chen.web.config.SiteConfig;
import chen.web.controller.ResourceNotFoundException;
import chen.web.controller.UbbParserFactory;
import chen.web.controller.forum.form.ReplyForm;
import chen.web.controller.forum.form.SubjectForm;
import chen.web.favorite.Showable;
import chen.web.forum.Category;
import chen.web.forum.CategoryManager;
import chen.web.forum.ReplyCondition;
import chen.web.forum.SubjectCondition;
import chen.web.forum.NotLowestException;
import chen.web.forum.Reply;
import chen.web.forum.Subject;
import chen.web.forum.TopicInfo;
import chen.web.forum.TopicManager;
import chen.web.forum.SubjectCondition.Order;
import chen.web.forum.SubjectCondition.OrderBy;
import chen.web.forum.SubjectCondition.SubjectType;
import chen.web.forum.Subject.TopRank;
import chen.web.util.NumberEncryption;
import chen.web.util.Pagination;
import chen.web.widget.vote.Vote;

public class TopicController implements Showable{
	
	private static final int Post_Count_Per_Page = 15;
	private static final int Topic_Info_Count_Per_Page = 30;
	
	private @Autowired TopicManager tm;
	private @Autowired CategoryManager cm;
	private @Autowired Validator validator;
	
	//发表主题
	@RequestMapping(value="/topic", method=RequestMethod.POST)
	public @ResponseBody JSONResponse post(@RequestBody SubjectForm bean) throws JSONException{
		Set<ConstraintViolation<SubjectForm>> failures = validator.validate(bean);
		if(!failures.isEmpty()){
			return JSONResponse.invalid(failures);
		}
		Category category = cm.get(bean.getCategoryId());
		if(category == null){
			throw new ResourceNotFoundException();
		}
		Subject subject = new Subject(category);
		subject.setContent(bean.getContent());
		subject.setTitle(bean.getTitle());
		subject.setWidget(bean.getWidget());
		try {
			subject = tm.post(subject);
		} catch (NotLowestException e) {
			// TODO Auto-generated catch block
			return JSONResponse.error(JSONError.create("categoryId", "请选择最底层的栏目发表"));
		}
		return JSONResponse.successed(showURL(subject));
	}
	
	//发表主题页面
	@RequestMapping(value="/subject/new", method=RequestMethod.GET)
	public String newSubject(@RequestParam(required=false) String type, @RequestParam(required=false) Integer categoryId, Model model) throws JSONException{
		Category category = null;
		if(categoryId != null){
			category = cm.get(categoryId);
		} else {
			category = cm.getRootCategory();
		}
		model.addAttribute("category", category);
		if("vote".equals(type)){
			return "post_vote_subject";
		}
		
		return "post_subject";
	}
	
	//发表回复
	@RequestMapping(value="/topic/{id}", method=RequestMethod.POST)
	public @ResponseBody JSONResponse post(@RequestBody ReplyForm bean, @PathVariable int id){
		Set<ConstraintViolation<ReplyForm>> failures = validator.validate(bean);
		if(!failures.isEmpty()){
			return JSONResponse.invalid(failures);
		}
		Subject sub = tm.getSubject(id);
		Reply reply = new Reply(sub);
		reply.setContent(bean.getContent());
		reply = tm.post(reply);
		reply.setContent(UbbParserFactory.standard().parse(reply.getContent()).toHtml());
		int pos = tm.replyPositionInTopic(reply);
		
		HashMap<String, Object> map = new HashMap<String, Object>();
		map.put("reply", reply);
		map.put("floor", pos+1);
		return JSONResponse.successed(map);
	}
	
	//编辑主题　
	@RequestMapping(value="/subject/{id}", method=RequestMethod.PUT)
	public @ResponseBody JSONResponse update(@RequestBody SubjectForm bean, @PathVariable int id) throws JSONException{
		Set<ConstraintViolation<SubjectForm>> failures = validator.validate(bean);
		if(!failures.isEmpty()){
			return JSONResponse.invalid(failures);
		}
		Subject subject = tm.getSubject(id);
		subject.setContent(bean.getContent());
		subject.setTitle(bean.getTitle());
		tm.update(subject);
		return JSONResponse.successed(showURL(subject));
	}
	
	//编辑主题页面　
	@RequestMapping(value="/subject/{id}/edit", method=RequestMethod.GET)
	public String edit(@PathVariable int id, Model model) throws JSONException{
		Subject subject = tm.getSubject(id);
		if(subject == null){
			throw new ResourceNotFoundException();
		}
		model.addAttribute("subject",subject);
		return "edit_subject";
	}
	
	//编辑回复
	@RequestMapping(value="/reply/{id}", method=RequestMethod.PUT)
	public @ResponseBody JSONResponse update(@RequestBody ReplyForm bean, @PathVariable int id){
		Set<ConstraintViolation<ReplyForm>> failures = validator.validate(bean);
		if(!failures.isEmpty()){
			return JSONResponse.invalid(failures);
		}

		Reply reply = tm.getReply(id);
		reply.setContent(bean.getContent());
		tm.update(reply);
		return JSONResponse.successed(showURL(reply));
	}
	
	//编辑回复页面
	@RequestMapping(value="/reply/{id}/edit", method=RequestMethod.GET)
	public String editReply(@PathVariable int id, Model model){
		Reply reply = tm.getReply(id);
		if(reply == null){
			throw new ResourceNotFoundException();
		}
		model.addAttribute("reply",reply);
		return "edit_reply";
	}
	
	//显示帖子，帖子的id是经过编码的
	@RequestMapping(value="/topic/{id}", method=RequestMethod.GET)
	public ModelAndView show(@PathVariable String id, @RequestParam(required=false) Integer page, @RequestHeader(value="Referer",required=false) String referer){
		if(page != null && page == 1){	//永久重定向
			RedirectView redirectView = new RedirectView("./" + id);
			redirectView.setStatusCode(HttpStatus.MOVED_PERMANENTLY);
			ModelAndView mav = new ModelAndView(redirectView);
			return mav;
		}
		
		Subject subject = tm.getSubject(NumberEncryption.decrypt(id));
		if(subject == null){
			throw new ResourceNotFoundException();
		}
		tm.increaseViewCount(subject);
		Pagination pagination = new Pagination(subject.getReplyCount()+1, Post_Count_Per_Page);
		List<Reply> replyList = null;
		ReplyCondition condition = new ReplyCondition().setSubject(subject);
		if(pagination.getCurrentPage() == 1){		//由于第一页有主题，回复少显示一楼，保证每页楼层数一致
			replyList = tm.getReplyList(condition, 0, Post_Count_Per_Page-1);
		} else {
			replyList = tm.getReplyList(condition, pagination.getBeginOffset()-1, Post_Count_Per_Page);
		}
		ModelAndView mav = new ModelAndView("topic");
		if(subject.getWidget() != null && subject.getWidget() instanceof Vote){
			Vote vote = (Vote)subject.getWidget();
			mav.addObject("hasVoted", vote.hasVoted());
		}
		
		String baseURL = ConfigRepository.instance().get(SiteConfig.class).getBaseURL();
		//判断是否来自栏目列表分页页面
		if(referer != null && referer.matches(baseURL + "/\\d+\\?page=\\d+")){
			mav.addObject("categoryURL", referer);
		}
		
		mav.addObject("subject", subject);
		mav.addObject("replyList", replyList);
		mav.addObject("pagination", pagination);
		mav.addObject("channel", "topic");
		return mav;
	}
	
	//显示回复，定位到回复所在的帖子
	@RequestMapping(value="/reply/{id}", method=RequestMethod.GET)
	public String showReply(@PathVariable int id){
		Reply reply = tm.getReply(id);
		if(reply == null){
			throw new ResourceNotFoundException("不存在此回复，或者已经被删除");
		}
		int order = tm.replyPositionInTopic(reply);
		int floor = order + 1;
		int pageNumber = (int) Math.ceil((double)floor/Post_Count_Per_Page);
		
		StringBuffer sb = new StringBuffer().append("redirect:")
											.append(showURL(reply.getBelongSubject()));
		if(pageNumber > 1){
			sb.append("?page=").append(pageNumber);
		}
		sb.append("#reply").append(reply.getId());
							
		return sb.toString();
	}
	
	@Override
	public String showURL(Object obj) {
		// TODO Auto-generated method stub
		if(obj == null){
			throw new NullPointerException();
		}
		SiteConfig config = ConfigRepository.instance().get(SiteConfig.class);
		if(obj instanceof Subject){
			return config.getBaseURL() + "/topic/" + NumberEncryption.encrypt(((Subject)obj).getId());
		}
		if(obj instanceof Reply){
			return config.getBaseURL() + "/reply/" + ((Reply)obj).getId();
		}
		if(obj instanceof TopicInfo){
			return config.getBaseURL() + "/topic/" + NumberEncryption.encrypt(((TopicInfo)obj).getId());
		}
		throw new RuntimeException("not support class :" + obj.getClass());
	}

	@Override
	public boolean support(Object obj) {
		// TODO Auto-generated method stub
		if(obj instanceof Subject || obj instanceof Reply || obj instanceof TopicInfo){
			return true;
		}
		return false;
	}
	
	//显示栏目中的帖子列表，把置顶的帖子放到第一页的最顶部
	@RequestMapping(value="/{path}", method=RequestMethod.GET)
	public String listTopicInfo(@PathVariable String path, Model model){
		Category category = getCategory(path);
		SubjectCondition cond = new SubjectCondition().setCategory(category).setContainsChildren(true).setType(SubjectType.not_top);
		int itemAmount = tm.totalSubject(cond);
		Pagination pagination = new Pagination(itemAmount, Topic_Info_Count_Per_Page);
		List<TopicInfo> topicInfoList = tm.getTopicInfoList(cond.setOrderBy(OrderBy.LastReplyDate).setOrder(Order.Desc), pagination.getBeginOffset(), pagination.getPageInfoCount());
		//在第一页最上方放置顶帖子
		if(pagination.getCurrentPage() == 1){
			cond = new SubjectCondition().setCategory(category)
								.setContainsChildren(true)
								.setType(SubjectType.top)
								.setOrderBy(OrderBy.LastReplyDate)
								.setOrder(Order.Desc);
			List<TopicInfo> topTopicInfoList = tm.getTopicInfoList(cond, 0, 10);
			topicInfoList.addAll(0, topTopicInfoList);	
		}
		model.addAttribute("topicInfoList", topicInfoList);
		model.addAttribute("pagination", pagination);
		model.addAttribute("category", cond.getCategory());
		return "topic_list";
	}
	
	//显示栏目中的精华帖子列表
	@RequestMapping(value="/{path}/digest", method=RequestMethod.GET)
	public String listDigestTopic(@PathVariable String path, Model model){
		Category category = getCategory(path);
		SubjectCondition cond = new SubjectCondition().setType(SubjectType.good).setCategory(category).setContainsChildren(true);
		addAttribute(model, cond);
		return "digest_topic_list";
	}
	
	//显示栏目中的投票帖子列表
	@RequestMapping(value="/{path}/vote", method=RequestMethod.GET)
	public String listVoteTopic(@PathVariable String path, Model model){
		Category category = getCategory(path);
		SubjectCondition cond = new SubjectCondition().setCategory(category).setContainsChildren(true).setWidgetClass(Vote.class);
		addAttribute(model, cond);
		return "vote_topic_list";
	}
	
	private Category getCategory(String path){
		Category category = cm.getByPath("/"+path);
		if(category == null){
			throw new ResourceNotFoundException();
		}
		return category;
	}
	
	private void addAttribute(Model model, SubjectCondition cond){
		int itemAmount = tm.totalSubject(cond);
		Pagination pagination = new Pagination(itemAmount, Topic_Info_Count_Per_Page);
		
		List<TopicInfo> topicInfoList = tm.getTopicInfoList(cond.setOrderBy(OrderBy.LastReplyDate).setOrder(Order.Desc), pagination.getBeginOffset(), pagination.getPageInfoCount());
	
		model.addAttribute("topicInfoList", topicInfoList);
		model.addAttribute("pagination", pagination);
		model.addAttribute("category", cond.getCategory());
	}
	
	//批量删除主题，id用下划线隔开
	@RequestMapping(value="/topic/{ids}", method=RequestMethod.DELETE)
	public @ResponseBody JSONResponse deleteTopic(@PathVariable String ids){
		String[] idArr= ids.split("_");
		for(String id : idArr){
			Subject subject = tm.getSubject(Integer.parseInt(id));
			if(subject == null){
				continue;
			}
			tm.safeDelete(subject);
		}
		return JSONResponse.successed();
	}
	
	//批量删除回复，id用下划线隔开
	@RequestMapping(value="/reply/{ids}", method=RequestMethod.DELETE)
	public @ResponseBody JSONResponse deleteReply(@PathVariable String ids){
		String[] idArr= ids.split("_");
		for(String id : idArr){
			Reply reply = tm.getReply(Integer.parseInt(id));
			if(reply == null){
				continue;
			}
			tm.safeDelete(reply);
		}
		return JSONResponse.successed();
	}
	
	//批量设置精华，id用下划线隔开.如果已经是精华则解除精华
	@RequestMapping(value="/topic/{ids}/set-digest", method=RequestMethod.PUT)
	public @ResponseBody JSONResponse digestTopic(@PathVariable String ids){
		String[] idArr= ids.split("_");
		for(String id : idArr){
			Subject subject = tm.getSubject(Integer.parseInt(id));
			if(subject == null){
				continue;
			}
			tm.setDigest(subject, !subject.isDigest());
		}
		return JSONResponse.successed();
	}
	
	//批量置顶，置顶级别为1, id用下划线隔开.如果已经是置顶则解除置顶
	@RequestMapping(value="/topic/{ids}/set-top", method=RequestMethod.PUT)
	public @ResponseBody JSONResponse topTopic(@PathVariable String ids){
		String[] idArr= ids.split("_");
		for(String id : idArr){
			Subject subject = tm.getSubject(Integer.parseInt(id));
			if(subject == null){
				continue;
			}
			if(subject.isTop()){
				tm.cancelTop(subject);
			} else {
				tm.setTop(subject, Subject.TopRank.one);
			}
		}
		return JSONResponse.successed();
	}

	//调整主题置顶的级别，每次增加1, 当主题的置顶级别已经是最高时，复位到0, 也就是最低级别
	@RequestMapping(value="/topic/{id}/roll-top-rank", method=RequestMethod.PUT)
	public @ResponseBody JSONResponse rollTopRank(@PathVariable int id){
		Subject subject = tm.getSubject(id);
		if(subject == null || !subject.isTop()){
			return JSONResponse.errorMessage("不存在此主题或者此主题未被置顶");
		}
		TopRank[] ranks = Subject.TopRank.values();
		int index = -1;
		for(TopRank rank : ranks){
			++ index;
			if(rank.equals(subject.getTopRank())){
				break;
			}
		}
		int rollIndex = index+1==ranks.length ? 0 : index+1;
		tm.setTop(subject, ranks[rollIndex]);
		return JSONResponse.successed();
	}
	
	//批量移动主题， id用下划线隔开
	@RequestMapping(value="/topic/{ids}/move", method=RequestMethod.PUT)
	public @ResponseBody JSONResponse move(@PathVariable String ids, @RequestParam int categoryId){
		Category category = cm.get(categoryId);
		if(category == null){
			throw new ResourceNotFoundException();
		}
		String[] idArr= ids.split("_");
		for(String id : idArr){
			Subject subject = tm.getSubject(Integer.parseInt(id));
			if(subject != null){
				try {
					tm.move(subject, category);
				} catch (NotLowestException e) {
					// TODO Auto-generated catch block
					return JSONResponse.errorMessage("不能将主题移动到一个非底层的栏目");
				}	
			}
		}
		return JSONResponse.successed();
	}
	
	//隐藏主题内容
	@RequestMapping(value="/subject/{id}/hide", method=RequestMethod.PUT)
	public @ResponseBody JSONResponse hideSubject(@PathVariable int id){
		Subject subject = tm.getSubject(id);
		if(subject == null){
			throw new ResourceNotFoundException();
		}
		tm.hideSubjectContent(subject, !subject.isHide());
		return JSONResponse.successed();
	}
	
	//隐藏回复内容
	@RequestMapping(value="/reply/{id}/hide", method=RequestMethod.PUT)
	public @ResponseBody JSONResponse hideReply(@PathVariable int id){
		Reply reply = tm.getReply(id);
		if(reply == null){
			throw new ResourceNotFoundException();
		}
		tm.hideReplyContent(reply, !reply.isHide());
		return JSONResponse.successed();
	}

}
